using ICSharpCode.SharpZipLib.Checksums;
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.BZip2
{
	public class BZip2OutputStream : Stream
	{
		private struct StackElement
		{
			public int int_0;
			public int int_1;
			public int int_2;
		}
		private const int SETMASK = 2097152;
		private const int CLEARMASK = -2097153;
		private const int GREATER_ICOST = 15;
		private const int LESSER_ICOST = 0;
		private const int SMALL_THRESH = 20;
		private const int DEPTH_THRESH = 10;
		private const int QSORT_STACK_SIZE = 1000;
		private readonly int[] increments = new int[]
		{
			1,
			4,
			13,
			40,
			121,
			364,
			1093,
			3280,
			9841,
			29524,
			88573,
			265720,
			797161,
			2391484
		};
		private bool isStreamOwner = true;
		private int last;
		private int origPtr;
		private int blockSize100k;
		private bool blockRandomised;
		private int bytesOut;
		private int bsBuff;
		private int bsLive;
		private IChecksum mCrc = new StrangeCRC();
		private bool[] inUse = new bool[256];
		private int nInUse;
		private char[] seqToUnseq = new char[256];
		private char[] unseqToSeq = new char[256];
		private char[] selector = new char[18002];
		private char[] selectorMtf = new char[18002];
		private byte[] block;
		private int[] quadrant;
		private int[] zptr;
		private short[] szptr;
		private int[] ftab;
		private int nMTF;
		private int[] mtfFreq = new int[258];
		private int workFactor;
		private int workDone;
		private int workLimit;
		private bool firstAttempt;
		private int nBlocksRandomised;
		private int currentChar = -1;
		private int runLength;
		private uint blockCRC;
		private uint combinedCRC;
		private int allowableBlockSize;
		private Stream baseStream;
		private bool disposed_;
		public bool IsStreamOwner
		{
			get
			{
				return this.isStreamOwner;
			}
			set
			{
				this.isStreamOwner = value;
			}
		}
		public override bool CanRead
		{
			get
			{
				return false;
			}
		}
		public override bool CanSeek
		{
			get
			{
				return false;
			}
		}
		public override bool CanWrite
		{
			get
			{
				return this.baseStream.get_CanWrite();
			}
		}
		public override long Length
		{
			get
			{
				return this.baseStream.get_Length();
			}
		}
		public override long Position
		{
			get
			{
				return this.baseStream.get_Position();
			}
			set
			{
				throw new NotSupportedException("BZip2OutputStream position cannot be set");
			}
		}
		public int BytesWritten
		{
			get
			{
				return this.bytesOut;
			}
		}
		public BZip2OutputStream(Stream stream) : this(stream, 9)
		{
		}
		public BZip2OutputStream(Stream stream, int blockSize)
		{
			this.BsSetStream(stream);
			this.workFactor = 50;
			if (blockSize > 9)
			{
				blockSize = 9;
			}
			if (blockSize < 1)
			{
				blockSize = 1;
			}
			this.blockSize100k = blockSize;
			this.AllocateCompressStructures();
			this.Initialize();
			this.InitBlock();
		}
		~BZip2OutputStream()
		{
			this.Dispose(false);
		}
		public override long Seek(long offset, SeekOrigin origin)
		{
			throw new NotSupportedException("BZip2OutputStream Seek not supported");
		}
		public override void SetLength(long value)
		{
			throw new NotSupportedException("BZip2OutputStream SetLength not supported");
		}
		public override int ReadByte()
		{
			throw new NotSupportedException("BZip2OutputStream ReadByte not supported");
		}
		public override int Read(byte[] buffer, int offset, int count)
		{
			throw new NotSupportedException("BZip2OutputStream Read not supported");
		}
		public override void Write(byte[] buffer, int offset, int count)
		{
			if (buffer == null)
			{
				throw new ArgumentNullException("buffer");
			}
			if (offset < 0)
			{
				throw new ArgumentOutOfRangeException("offset");
			}
			if (count < 0)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			if (buffer.Length - offset < count)
			{
				throw new ArgumentException("Offset/count out of range");
			}
			for (int i = 0; i < count; i++)
			{
				this.WriteByte(buffer[offset + i]);
			}
		}
		public override void WriteByte(byte value)
		{
			int num = (256 + (int)value) % 256;
			if (this.currentChar != -1)
			{
				if (this.currentChar == num)
				{
					this.runLength++;
					if (this.runLength > 254)
					{
						this.WriteRun();
						this.currentChar = -1;
						this.runLength = 0;
					}
				}
				else
				{
					this.WriteRun();
					this.runLength = 1;
					this.currentChar = num;
				}
			}
			else
			{
				this.currentChar = num;
				this.runLength++;
			}
		}
		public override void Close()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}
		private void MakeMaps()
		{
			this.nInUse = 0;
			for (int i = 0; i < 256; i++)
			{
				if (this.inUse[i])
				{
					this.seqToUnseq[this.nInUse] = (char)i;
					this.unseqToSeq[i] = (char)this.nInUse;
					this.nInUse++;
				}
			}
		}
		private void WriteRun()
		{
			if (this.last < this.allowableBlockSize)
			{
				this.inUse[this.currentChar] = true;
				for (int i = 0; i < this.runLength; i++)
				{
					this.mCrc.Update(this.currentChar);
				}
				switch (this.runLength)
				{
				case 1:
					this.last++;
					this.block[this.last + 1] = (byte)this.currentChar;
					break;
				case 2:
					this.last++;
					this.block[this.last + 1] = (byte)this.currentChar;
					this.last++;
					this.block[this.last + 1] = (byte)this.currentChar;
					break;
				case 3:
					this.last++;
					this.block[this.last + 1] = (byte)this.currentChar;
					this.last++;
					this.block[this.last + 1] = (byte)this.currentChar;
					this.last++;
					this.block[this.last + 1] = (byte)this.currentChar;
					break;
				default:
					this.inUse[this.runLength - 4] = true;
					this.last++;
					this.block[this.last + 1] = (byte)this.currentChar;
					this.last++;
					this.block[this.last + 1] = (byte)this.currentChar;
					this.last++;
					this.block[this.last + 1] = (byte)this.currentChar;
					this.last++;
					this.block[this.last + 1] = (byte)this.currentChar;
					this.last++;
					this.block[this.last + 1] = (byte)(this.runLength - 4);
					break;
				}
			}
			else
			{
				this.EndBlock();
				this.InitBlock();
				this.WriteRun();
			}
		}
		protected override void Dispose(bool disposing)
		{
			try
			{
				base.Dispose(disposing);
				if (!this.disposed_)
				{
					this.disposed_ = true;
					if (this.runLength > 0)
					{
						this.WriteRun();
					}
					this.currentChar = -1;
					this.EndBlock();
					this.EndCompression();
					this.Flush();
				}
			}
			finally
			{
				if (disposing && this.IsStreamOwner)
				{
					this.baseStream.Close();
				}
			}
		}
		public override void Flush()
		{
			this.baseStream.Flush();
		}
		private void Initialize()
		{
			this.bytesOut = 0;
			this.nBlocksRandomised = 0;
			this.BsPutUChar(66);
			this.BsPutUChar(90);
			this.BsPutUChar(104);
			this.BsPutUChar(48 + this.blockSize100k);
			this.combinedCRC = 0u;
		}
		private void InitBlock()
		{
			this.mCrc.Reset();
			this.last = -1;
			for (int i = 0; i < 256; i++)
			{
				this.inUse[i] = false;
			}
			this.allowableBlockSize = 100000 * this.blockSize100k - 20;
		}
		private void EndBlock()
		{
			if (this.last >= 0)
			{
				this.blockCRC = (uint)this.mCrc.Value;
				this.combinedCRC = (this.combinedCRC << 1 | this.combinedCRC >> 31);
				this.combinedCRC ^= this.blockCRC;
				this.DoReversibleTransformation();
				this.BsPutUChar(49);
				this.BsPutUChar(65);
				this.BsPutUChar(89);
				this.BsPutUChar(38);
				this.BsPutUChar(83);
				this.BsPutUChar(89);
				this.BsPutint((int)this.blockCRC);
				if (this.blockRandomised)
				{
					this.BsW(1, 1);
					this.nBlocksRandomised++;
				}
				else
				{
					this.BsW(1, 0);
				}
				this.MoveToFrontCodeAndSend();
			}
		}
		private void EndCompression()
		{
			this.BsPutUChar(23);
			this.BsPutUChar(114);
			this.BsPutUChar(69);
			this.BsPutUChar(56);
			this.BsPutUChar(80);
			this.BsPutUChar(144);
			this.BsPutint((int)this.combinedCRC);
			this.BsFinishedWithStream();
		}
		private void BsSetStream(Stream stream)
		{
			this.baseStream = stream;
			this.bsLive = 0;
			this.bsBuff = 0;
			this.bytesOut = 0;
		}
		private void BsFinishedWithStream()
		{
			while (this.bsLive > 0)
			{
				int num = this.bsBuff >> 24;
				this.baseStream.WriteByte((byte)num);
				this.bsBuff <<= 8;
				this.bsLive -= 8;
				this.bytesOut++;
			}
		}
		private void BsW(int int_0, int int_1)
		{
			while (this.bsLive >= 8)
			{
				int num = this.bsBuff >> 24;
				this.baseStream.WriteByte((byte)num);
				this.bsBuff <<= 8;
				this.bsLive -= 8;
				this.bytesOut++;
			}
			this.bsBuff |= int_1 << 32 - this.bsLive - int_0;
			this.bsLive += int_0;
		}
		private void BsPutUChar(int int_0)
		{
			this.BsW(8, int_0);
		}
		private void BsPutint(int int_0)
		{
			this.BsW(8, int_0 >> 24 & 255);
			this.BsW(8, int_0 >> 16 & 255);
			this.BsW(8, int_0 >> 8 & 255);
			this.BsW(8, int_0 & 255);
		}
		private void BsPutIntVS(int numBits, int int_0)
		{
			this.BsW(numBits, int_0);
		}
		private void SendMTFValues()
		{
			char[][] array = new char[6][];
			for (int i = 0; i < 6; i++)
			{
				array[i] = new char[258];
			}
			int num = 0;
			int num2 = this.nInUse + 2;
			for (int j = 0; j < 6; j++)
			{
				for (int k = 0; k < num2; k++)
				{
					array[j][k] = '\u000f';
				}
			}
			if (this.nMTF <= 0)
			{
				BZip2OutputStream.Panic();
			}
			int num3;
			if (this.nMTF < 200)
			{
				num3 = 2;
			}
			else
			{
				if (this.nMTF < 600)
				{
					num3 = 3;
				}
				else
				{
					if (this.nMTF < 1200)
					{
						num3 = 4;
					}
					else
					{
						if (this.nMTF < 2400)
						{
							num3 = 5;
						}
						else
						{
							num3 = 6;
						}
					}
				}
			}
			int l = num3;
			int num4 = this.nMTF;
			int m = 0;
			while (l > 0)
			{
				int num5 = num4 / l;
				int num6 = 0;
				int num7 = m - 1;
				while (num6 < num5 && num7 < num2 - 1)
				{
					num7++;
					num6 += this.mtfFreq[num7];
				}
				if (num7 > m && l != num3 && l != 1 && (num3 - l) % 2 == 1)
				{
					num6 -= this.mtfFreq[num7];
					num7--;
				}
				for (int k = 0; k < num2; k++)
				{
					if (k >= m && k <= num7)
					{
						array[l - 1][k] = '\0';
					}
					else
					{
						array[l - 1][k] = '\u000f';
					}
				}
				l--;
				m = num7 + 1;
				num4 -= num6;
			}
			int[][] array2 = new int[6][];
			for (int i = 0; i < 6; i++)
			{
				array2[i] = new int[258];
			}
			int[] array3 = new int[6];
			short[] array4 = new short[6];
			for (int n = 0; n < 4; n++)
			{
				for (int j = 0; j < num3; j++)
				{
					array3[j] = 0;
				}
				for (int j = 0; j < num3; j++)
				{
					for (int k = 0; k < num2; k++)
					{
						array2[j][k] = 0;
					}
				}
				num = 0;
				int num8 = 0;
				int num7;
				for (m = 0; m < this.nMTF; m = num7 + 1)
				{
					num7 = m + 50 - 1;
					if (num7 >= this.nMTF)
					{
						num7 = this.nMTF - 1;
					}
					for (int j = 0; j < num3; j++)
					{
						array4[j] = 0;
					}
					if (num3 == 6)
					{
						short num14;
						short num13;
						short num12;
						short num11;
						short num10;
						short num9 = num10 = (num11 = (num12 = (num13 = (num14 = 0))));
						for (int i = m; i <= num7; i++)
						{
							short num15 = this.szptr[i];
							num10 += (short)array[0][(int)num15];
							num9 += (short)array[1][(int)num15];
							num11 += (short)array[2][(int)num15];
							num12 += (short)array[3][(int)num15];
							num13 += (short)array[4][(int)num15];
							num14 += (short)array[5][(int)num15];
						}
						array4[0] = num10;
						array4[1] = num9;
						array4[2] = num11;
						array4[3] = num12;
						array4[4] = num13;
						array4[5] = num14;
					}
					else
					{
						for (int i = m; i <= num7; i++)
						{
							short num15 = this.szptr[i];
							for (int j = 0; j < num3; j++)
							{
								short[] expr_321_cp_0 = array4;
								int expr_321_cp_1 = j;
								expr_321_cp_0[expr_321_cp_1] += (short)array[j][(int)num15];
							}
						}
					}
					int num16 = 999999999;
					int num17 = -1;
					for (int j = 0; j < num3; j++)
					{
						if ((int)array4[j] < num16)
						{
							num16 = (int)array4[j];
							num17 = j;
						}
					}
					num8 += num16;
					array3[num17]++;
					this.selector[num] = (char)num17;
					num++;
					for (int i = m; i <= num7; i++)
					{
						array2[num17][(int)this.szptr[i]]++;
					}
				}
				for (int j = 0; j < num3; j++)
				{
					BZip2OutputStream.HbMakeCodeLengths(array[j], array2[j], num2, 20);
				}
			}
			if (num3 >= 8)
			{
				BZip2OutputStream.Panic();
			}
			if (num >= 32768 || num > 18002)
			{
				BZip2OutputStream.Panic();
			}
			char[] array5 = new char[6];
			for (int i = 0; i < num3; i++)
			{
				array5[i] = (char)i;
			}
			for (int i = 0; i < num; i++)
			{
				char c = this.selector[i];
				int num18 = 0;
				char c2 = array5[0];
				while (c != c2)
				{
					num18++;
					char c3 = c2;
					c2 = array5[num18];
					array5[num18] = c3;
				}
				array5[0] = c2;
				this.selectorMtf[i] = (char)num18;
			}
			int[][] array6 = new int[6][];
			for (int i = 0; i < 6; i++)
			{
				array6[i] = new int[258];
			}
			for (int j = 0; j < num3; j++)
			{
				int num19 = 32;
				int num20 = 0;
				for (int i = 0; i < num2; i++)
				{
					if ((int)array[j][i] > num20)
					{
						num20 = (int)array[j][i];
					}
					if ((int)array[j][i] < num19)
					{
						num19 = (int)array[j][i];
					}
				}
				if (num20 > 20)
				{
					BZip2OutputStream.Panic();
				}
				if (num19 < 1)
				{
					BZip2OutputStream.Panic();
				}
				BZip2OutputStream.HbAssignCodes(array6[j], array[j], num19, num20, num2);
			}
			bool[] array7 = new bool[16];
			for (int i = 0; i < 16; i++)
			{
				array7[i] = false;
				for (int num18 = 0; num18 < 16; num18++)
				{
					if (this.inUse[i * 16 + num18])
					{
						array7[i] = true;
					}
				}
			}
			for (int i = 0; i < 16; i++)
			{
				if (array7[i])
				{
					this.BsW(1, 1);
				}
				else
				{
					this.BsW(1, 0);
				}
			}
			for (int i = 0; i < 16; i++)
			{
				if (array7[i])
				{
					for (int num18 = 0; num18 < 16; num18++)
					{
						if (this.inUse[i * 16 + num18])
						{
							this.BsW(1, 1);
						}
						else
						{
							this.BsW(1, 0);
						}
					}
				}
			}
			this.BsW(3, num3);
			this.BsW(15, num);
			for (int i = 0; i < num; i++)
			{
				for (int num18 = 0; num18 < (int)this.selectorMtf[i]; num18++)
				{
					this.BsW(1, 1);
				}
				this.BsW(1, 0);
			}
			for (int j = 0; j < num3; j++)
			{
				int num21 = (int)array[j][0];
				this.BsW(5, num21);
				for (int i = 0; i < num2; i++)
				{
					while (num21 < (int)array[j][i])
					{
						this.BsW(2, 2);
						num21++;
					}
					while (num21 > (int)array[j][i])
					{
						this.BsW(2, 3);
						num21--;
					}
					this.BsW(1, 0);
				}
			}
			int num22 = 0;
			m = 0;
			while (m < this.nMTF)
			{
				int num7 = m + 50 - 1;
				if (num7 >= this.nMTF)
				{
					num7 = this.nMTF - 1;
				}
				for (int i = m; i <= num7; i++)
				{
					this.BsW((int)array[(int)this.selector[num22]][(int)this.szptr[i]], array6[(int)this.selector[num22]][(int)this.szptr[i]]);
				}
				m = num7 + 1;
				num22++;
			}
			if (num22 != num)
			{
				BZip2OutputStream.Panic();
			}
		}
		private void MoveToFrontCodeAndSend()
		{
			this.BsPutIntVS(24, this.origPtr);
			this.GenerateMTFValues();
			this.SendMTFValues();
		}
		private void SimpleSort(int int_0, int int_1, int int_2)
		{
			int num = int_1 - int_0 + 1;
			if (num >= 2)
			{
				int i = 0;
				while (this.increments[i] < num)
				{
					i++;
				}
				for (i--; i >= 0; i--)
				{
					int num2 = this.increments[i];
					int j = int_0 + num2;
					while (j <= int_1)
					{
						int num3 = this.zptr[j];
						int num4 = j;
						while (this.FullGtU(this.zptr[num4 - num2] + int_2, num3 + int_2))
						{
							this.zptr[num4] = this.zptr[num4 - num2];
							num4 -= num2;
							if (num4 <= int_0 + num2 - 1)
							{
								break;
							}
						}
						this.zptr[num4] = num3;
						j++;
						if (j > int_1)
						{
							break;
						}
						num3 = this.zptr[j];
						num4 = j;
						while (this.FullGtU(this.zptr[num4 - num2] + int_2, num3 + int_2))
						{
							this.zptr[num4] = this.zptr[num4 - num2];
							num4 -= num2;
							if (num4 <= int_0 + num2 - 1)
							{
								break;
							}
						}
						this.zptr[num4] = num3;
						j++;
						if (j > int_1)
						{
							break;
						}
						num3 = this.zptr[j];
						num4 = j;
						while (this.FullGtU(this.zptr[num4 - num2] + int_2, num3 + int_2))
						{
							this.zptr[num4] = this.zptr[num4 - num2];
							num4 -= num2;
							if (num4 <= int_0 + num2 - 1)
							{
								break;
							}
						}
						this.zptr[num4] = num3;
						j++;
						if (this.workDone > this.workLimit && this.firstAttempt)
						{
							return;
						}
					}
				}
			}
		}
		private void Vswap(int int_0, int int_1, int int_2)
		{
			while (int_2 > 0)
			{
				int num = this.zptr[int_0];
				this.zptr[int_0] = this.zptr[int_1];
				this.zptr[int_1] = num;
				int_0++;
				int_1++;
				int_2--;
			}
		}
		private void QSort3(int loSt, int hiSt, int dSt)
		{
			BZip2OutputStream.StackElement[] array = new BZip2OutputStream.StackElement[1000];
			array[0].int_0 = loSt;
			array[0].int_1 = hiSt;
			array[0].int_2 = dSt;
			int i = 0 + 1;
			while (i > 0)
			{
				if (i >= 1000)
				{
					BZip2OutputStream.Panic();
				}
				i--;
				int int_ = array[i].int_0;
				int int_2 = array[i].int_1;
				int int_3 = array[i].int_2;
				if (int_2 - int_ < 20 || int_3 > 10)
				{
					this.SimpleSort(int_, int_2, int_3);
					if (this.workDone > this.workLimit && this.firstAttempt)
					{
						break;
					}
				}
				else
				{
					int num = (int)BZip2OutputStream.Med3(this.block[this.zptr[int_] + int_3 + 1], this.block[this.zptr[int_2] + int_3 + 1], this.block[this.zptr[int_ + int_2 >> 1] + int_3 + 1]);
					int j;
					int num2 = j = int_;
					int num4;
					int num3 = num4 = int_2;
					while (true)
					{
						int num6;
						if (j <= num4)
						{
							int num5 = (int)this.block[this.zptr[j] + int_3 + 1] - num;
							if (num5 == 0)
							{
								num6 = this.zptr[j];
								this.zptr[j] = this.zptr[num2];
								this.zptr[num2] = num6;
								num2++;
								j++;
								continue;
							}
							if (num5 <= 0)
							{
								j++;
								continue;
							}
						}
						while (j <= num4)
						{
							int num5 = (int)this.block[this.zptr[num4] + int_3 + 1] - num;
							if (num5 == 0)
							{
								num6 = this.zptr[num4];
								this.zptr[num4] = this.zptr[num3];
								this.zptr[num3] = num6;
								num3--;
								num4--;
							}
							else
							{
								if (num5 < 0)
								{
									break;
								}
								num4--;
							}
						}
						if (j > num4)
						{
							break;
						}
						num6 = this.zptr[j];
						this.zptr[j] = this.zptr[num4];
						this.zptr[num4] = num6;
						j++;
						num4--;
					}
					if (num3 < num2)
					{
						array[i].int_0 = int_;
						array[i].int_1 = int_2;
						array[i].int_2 = int_3 + 1;
						i++;
					}
					else
					{
						int num5 = (num2 - int_ < j - num2) ? (num2 - int_) : (j - num2);
						this.Vswap(int_, j - num5, num5);
						int num7 = (int_2 - num3 < num3 - num4) ? (int_2 - num3) : (num3 - num4);
						this.Vswap(j, int_2 - num7 + 1, num7);
						num5 = int_ + j - num2 - 1;
						num7 = int_2 - (num3 - num4) + 1;
						array[i].int_0 = int_;
						array[i].int_1 = num5;
						array[i].int_2 = int_3;
						i++;
						array[i].int_0 = num5 + 1;
						array[i].int_1 = num7 - 1;
						array[i].int_2 = int_3 + 1;
						i++;
						array[i].int_0 = num7;
						array[i].int_1 = int_2;
						array[i].int_2 = int_3;
						i++;
					}
				}
			}
		}
		private void MainSort()
		{
			int[] array = new int[256];
			int[] array2 = new int[256];
			bool[] array3 = new bool[256];
			for (int i = 0; i < 20; i++)
			{
				this.block[this.last + i + 2] = this.block[i % (this.last + 1) + 1];
			}
			for (int i = 0; i <= this.last + 20; i++)
			{
				this.quadrant[i] = 0;
			}
			this.block[0] = this.block[this.last + 1];
			if (this.last < 4000)
			{
				for (int i = 0; i <= this.last; i++)
				{
					this.zptr[i] = i;
				}
				this.firstAttempt = false;
				this.workLimit = 0;
				this.workDone = 0;
				this.SimpleSort(0, this.last, 0);
			}
			else
			{
				int num = 0;
				for (int i = 0; i <= 255; i++)
				{
					array3[i] = false;
				}
				for (int i = 0; i <= 65536; i++)
				{
					this.ftab[i] = 0;
				}
				int num2 = (int)this.block[0];
				for (int i = 0; i <= this.last; i++)
				{
					int num3 = (int)this.block[i + 1];
					this.ftab[(num2 << 8) + num3]++;
					num2 = num3;
				}
				for (int i = 1; i <= 65536; i++)
				{
					this.ftab[i] += this.ftab[i - 1];
				}
				num2 = (int)this.block[1];
				int j;
				for (int i = 0; i < this.last; i++)
				{
					int num3 = (int)this.block[i + 2];
					j = (num2 << 8) + num3;
					num2 = num3;
					this.ftab[j]--;
					this.zptr[this.ftab[j]] = i;
				}
				j = ((int)this.block[this.last + 1] << 8) + (int)this.block[1];
				this.ftab[j]--;
				this.zptr[this.ftab[j]] = this.last;
				for (int i = 0; i <= 255; i++)
				{
					array[i] = i;
				}
				int num4 = 1;
				do
				{
					num4 = 3 * num4 + 1;
				}
				while (num4 <= 256);
				do
				{
					num4 /= 3;
					for (int i = num4; i <= 255; i++)
					{
						int num5 = array[i];
						j = i;
						while (this.ftab[array[j - num4] + 1 << 8] - this.ftab[array[j - num4] << 8] > this.ftab[num5 + 1 << 8] - this.ftab[num5 << 8])
						{
							array[j] = array[j - num4];
							j -= num4;
							if (j <= num4 - 1)
							{
								break;
							}
						}
						array[j] = num5;
					}
				}
				while (num4 != 1);
				for (int i = 0; i <= 255; i++)
				{
					int num6 = array[i];
					for (j = 0; j <= 255; j++)
					{
						int num7 = (num6 << 8) + j;
						if ((this.ftab[num7] & 2097152) != 2097152)
						{
							int num8 = this.ftab[num7] & -2097153;
							int num9 = (this.ftab[num7 + 1] & -2097153) - 1;
							if (num9 > num8)
							{
								this.QSort3(num8, num9, 2);
								num += num9 - num8 + 1;
								if (this.workDone > this.workLimit && this.firstAttempt)
								{
									return;
								}
							}
							this.ftab[num7] |= 2097152;
						}
					}
					array3[num6] = true;
					if (i < 255)
					{
						int num10 = this.ftab[num6 << 8] & -2097153;
						int num11 = (this.ftab[num6 + 1 << 8] & -2097153) - num10;
						int num12 = 0;
						while (num11 >> num12 > 65534)
						{
							num12++;
						}
						for (j = 0; j < num11; j++)
						{
							int num13 = this.zptr[num10 + j];
							int num14 = j >> num12;
							this.quadrant[num13] = num14;
							if (num13 < 20)
							{
								this.quadrant[num13 + this.last + 1] = num14;
							}
						}
						if (num11 - 1 >> num12 > 65535)
						{
							BZip2OutputStream.Panic();
						}
					}
					for (j = 0; j <= 255; j++)
					{
						array2[j] = (this.ftab[(j << 8) + num6] & -2097153);
					}
					for (j = (this.ftab[num6 << 8] & -2097153); j < (this.ftab[num6 + 1 << 8] & -2097153); j++)
					{
						num2 = (int)this.block[this.zptr[j]];
						if (!array3[num2])
						{
							this.zptr[array2[num2]] = ((this.zptr[j] == 0) ? this.last : (this.zptr[j] - 1));
							array2[num2]++;
						}
					}
					for (j = 0; j <= 255; j++)
					{
						this.ftab[(j << 8) + num6] |= 2097152;
					}
				}
			}
		}
		private void RandomiseBlock()
		{
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < 256; i++)
			{
				this.inUse[i] = false;
			}
			for (int i = 0; i <= this.last; i++)
			{
				if (num == 0)
				{
					num = BZip2Constants.RandomNumbers[num2];
					num2++;
					if (num2 == 512)
					{
						num2 = 0;
					}
				}
				num--;
				byte[] expr_5C_cp_0 = this.block;
				int expr_5C_cp_1 = i + 1;
				expr_5C_cp_0[expr_5C_cp_1] ^= ((num == 1) ? 1 : 0);
				byte[] expr_80_cp_0 = this.block;
				int expr_80_cp_1 = i + 1;
				expr_80_cp_0[expr_80_cp_1] &= 255;
				this.inUse[(int)this.block[i + 1]] = true;
			}
		}
		private void DoReversibleTransformation()
		{
			this.workLimit = this.workFactor * this.last;
			this.workDone = 0;
			this.blockRandomised = false;
			this.firstAttempt = true;
			this.MainSort();
			if (this.workDone > this.workLimit && this.firstAttempt)
			{
				this.RandomiseBlock();
				this.workDone = 0;
				this.workLimit = 0;
				this.blockRandomised = true;
				this.firstAttempt = false;
				this.MainSort();
			}
			this.origPtr = -1;
			for (int i = 0; i <= this.last; i++)
			{
				if (this.zptr[i] == 0)
				{
					this.origPtr = i;
					IL_A8:
					if (this.origPtr == -1)
					{
						BZip2OutputStream.Panic();
					}
					return;
				}
			}
			goto IL_A8;
		}
		private bool FullGtU(int int_0, int int_1)
		{
			byte b = this.block[int_0 + 1];
			byte b2 = this.block[int_1 + 1];
			bool result;
			if (b != b2)
			{
				result = (b > b2);
			}
			else
			{
				int_0++;
				int_1++;
				b = this.block[int_0 + 1];
				b2 = this.block[int_1 + 1];
				if (b != b2)
				{
					result = (b > b2);
				}
				else
				{
					int_0++;
					int_1++;
					b = this.block[int_0 + 1];
					b2 = this.block[int_1 + 1];
					if (b != b2)
					{
						result = (b > b2);
					}
					else
					{
						int_0++;
						int_1++;
						b = this.block[int_0 + 1];
						b2 = this.block[int_1 + 1];
						if (b != b2)
						{
							result = (b > b2);
						}
						else
						{
							int_0++;
							int_1++;
							b = this.block[int_0 + 1];
							b2 = this.block[int_1 + 1];
							if (b != b2)
							{
								result = (b > b2);
							}
							else
							{
								int_0++;
								int_1++;
								b = this.block[int_0 + 1];
								b2 = this.block[int_1 + 1];
								if (b != b2)
								{
									result = (b > b2);
								}
								else
								{
									int_0++;
									int_1++;
									int num = this.last + 1;
									int num2;
									int num3;
									while (true)
									{
										b = this.block[int_0 + 1];
										b2 = this.block[int_1 + 1];
										if (b != b2)
										{
											break;
										}
										num2 = this.quadrant[int_0];
										num3 = this.quadrant[int_1];
										if (num2 != num3)
										{
											goto IL_2A6;
										}
										int_0++;
										int_1++;
										b = this.block[int_0 + 1];
										b2 = this.block[int_1 + 1];
										if (b != b2)
										{
											goto IL_2AE;
										}
										num2 = this.quadrant[int_0];
										num3 = this.quadrant[int_1];
										if (num2 != num3)
										{
											goto IL_2B5;
										}
										int_0++;
										int_1++;
										b = this.block[int_0 + 1];
										b2 = this.block[int_1 + 1];
										if (b != b2)
										{
											goto IL_2BD;
										}
										num2 = this.quadrant[int_0];
										num3 = this.quadrant[int_1];
										if (num2 != num3)
										{
											goto IL_2C4;
										}
										int_0++;
										int_1++;
										b = this.block[int_0 + 1];
										b2 = this.block[int_1 + 1];
										if (b != b2)
										{
											goto IL_2CC;
										}
										num2 = this.quadrant[int_0];
										num3 = this.quadrant[int_1];
										if (num2 != num3)
										{
											goto IL_2D3;
										}
										int_0++;
										int_1++;
										if (int_0 > this.last)
										{
											int_0 -= this.last;
											int_0--;
										}
										if (int_1 > this.last)
										{
											int_1 -= this.last;
											int_1--;
										}
										num -= 4;
										this.workDone++;
										if (num < 0)
										{
											goto IL_2DB;
										}
									}
									result = (b > b2);
									return result;
									IL_2A6:
									result = (num2 > num3);
									return result;
									IL_2AE:
									result = (b > b2);
									return result;
									IL_2B5:
									result = (num2 > num3);
									return result;
									IL_2BD:
									result = (b > b2);
									return result;
									IL_2C4:
									result = (num2 > num3);
									return result;
									IL_2CC:
									result = (b > b2);
									return result;
									IL_2D3:
									result = (num2 > num3);
									return result;
									IL_2DB:
									result = false;
								}
							}
						}
					}
				}
			}
			return result;
		}
		private void AllocateCompressStructures()
		{
			int num = 100000 * this.blockSize100k;
			this.block = new byte[num + 1 + 20];
			this.quadrant = new int[num + 20];
			this.zptr = new int[num];
			this.ftab = new int[65537];
			if (this.block != null && this.quadrant != null && this.zptr != null && this.ftab != null)
			{
			}
			this.szptr = new short[2 * num];
		}
		private void GenerateMTFValues()
		{
			char[] array = new char[256];
			this.MakeMaps();
			int num = this.nInUse + 1;
			for (int i = 0; i <= num; i++)
			{
				this.mtfFreq[i] = 0;
			}
			int num2 = 0;
			int num3 = 0;
			for (int i = 0; i < this.nInUse; i++)
			{
				array[i] = (char)i;
			}
			for (int i = 0; i <= this.last; i++)
			{
				char c = this.unseqToSeq[(int)this.block[this.zptr[i]]];
				int num4 = 0;
				char c2 = array[0];
				while (c != c2)
				{
					num4++;
					char c3 = c2;
					c2 = array[num4];
					array[num4] = c3;
				}
				array[0] = c2;
				if (num4 == 0)
				{
					num3++;
				}
				else
				{
					if (num3 > 0)
					{
						num3--;
						while (true)
						{
							switch (num3 % 2)
							{
							case 0:
								this.szptr[num2] = 0;
								num2++;
								this.mtfFreq[0]++;
								break;
							case 1:
								this.szptr[num2] = 1;
								num2++;
								this.mtfFreq[1]++;
								break;
							}
							if (num3 < 2)
							{
								break;
							}
							num3 = (num3 - 2) / 2;
						}
						num3 = 0;
					}
					this.szptr[num2] = (short)(num4 + 1);
					num2++;
					this.mtfFreq[num4 + 1]++;
				}
			}
			if (num3 > 0)
			{
				num3--;
				while (true)
				{
					switch (num3 % 2)
					{
					case 0:
						this.szptr[num2] = 0;
						num2++;
						this.mtfFreq[0]++;
						break;
					case 1:
						this.szptr[num2] = 1;
						num2++;
						this.mtfFreq[1]++;
						break;
					}
					if (num3 < 2)
					{
						break;
					}
					num3 = (num3 - 2) / 2;
				}
			}
			this.szptr[num2] = (short)num;
			num2++;
			this.mtfFreq[num]++;
			this.nMTF = num2;
		}
		private static void Panic()
		{
			throw new BZip2Exception("BZip2 output stream panic");
		}
		private static void HbMakeCodeLengths(char[] char_0, int[] freq, int alphaSize, int maxLen)
		{
			int[] array = new int[260];
			int[] array2 = new int[516];
			int[] array3 = new int[516];
			for (int i = 0; i < alphaSize; i++)
			{
				array2[i + 1] = ((freq[i] == 0) ? 1 : freq[i]) << 8;
			}
			while (true)
			{
				int num = alphaSize;
				int j = 0;
				array[0] = 0;
				array2[0] = 0;
				array3[0] = -2;
				for (int i = 1; i <= alphaSize; i++)
				{
					array3[i] = -1;
					j++;
					array[j] = i;
					int num2 = j;
					int num3 = array[num2];
					while (array2[num3] < array2[array[num2 >> 1]])
					{
						array[num2] = array[num2 >> 1];
						num2 >>= 1;
					}
					array[num2] = num3;
				}
				if (j >= 260)
				{
					BZip2OutputStream.Panic();
				}
				while (j > 1)
				{
					int num4 = array[1];
					array[1] = array[j];
					j--;
					int num2 = 1;
					int num3 = array[1];
					while (true)
					{
						int num5 = num2 << 1;
						if (num5 > j)
						{
							break;
						}
						if (num5 < j && array2[array[num5 + 1]] < array2[array[num5]])
						{
							num5++;
						}
						if (array2[num3] < array2[array[num5]])
						{
							break;
						}
						array[num2] = array[num5];
						num2 = num5;
					}
					array[num2] = num3;
					int num6 = array[1];
					array[1] = array[j];
					j--;
					num2 = 1;
					num3 = array[1];
					while (true)
					{
						int num5 = num2 << 1;
						if (num5 > j)
						{
							break;
						}
						if (num5 < j && array2[array[num5 + 1]] < array2[array[num5]])
						{
							num5++;
						}
						if (array2[num3] < array2[array[num5]])
						{
							break;
						}
						array[num2] = array[num5];
						num2 = num5;
					}
					array[num2] = num3;
					num++;
					array3[num4] = (array3[num6] = num);
					array2[num] = ((int)(((long)array2[num4] & (long)((ulong)-256)) + ((long)array2[num6] & (long)((ulong)-256))) | 1 + (((array2[num4] & 255) > (array2[num6] & 255)) ? (array2[num4] & 255) : (array2[num6] & 255)));
					array3[num] = -1;
					j++;
					array[j] = num;
					num2 = j;
					num3 = array[num2];
					while (array2[num3] < array2[array[num2 >> 1]])
					{
						array[num2] = array[num2 >> 1];
						num2 >>= 1;
					}
					array[num2] = num3;
				}
				if (num >= 516)
				{
					BZip2OutputStream.Panic();
				}
				bool flag = false;
				for (int i = 1; i <= alphaSize; i++)
				{
					int num7 = 0;
					int num8 = i;
					while (array3[num8] >= 0)
					{
						num8 = array3[num8];
						num7++;
					}
					char_0[i - 1] = (char)num7;
					if (num7 > maxLen)
					{
						flag = true;
					}
				}
				if (!flag)
				{
					break;
				}
				for (int i = 1; i < alphaSize; i++)
				{
					int num7 = array2[i] >> 8;
					num7 = 1 + num7 / 2;
					array2[i] = num7 << 8;
				}
			}
		}
		private static void HbAssignCodes(int[] code, char[] length, int minLen, int maxLen, int alphaSize)
		{
			int num = 0;
			for (int i = minLen; i <= maxLen; i++)
			{
				for (int j = 0; j < alphaSize; j++)
				{
					if ((int)length[j] == i)
					{
						code[j] = num;
						num++;
					}
				}
				num <<= 1;
			}
		}
		private static byte Med3(byte byte_0, byte byte_1, byte byte_2)
		{
			if (byte_0 > byte_1)
			{
				byte b = byte_0;
				byte_0 = byte_1;
				byte_1 = b;
			}
			if (byte_1 > byte_2)
			{
				byte b = byte_1;
				byte_1 = byte_2;
				byte_2 = b;
			}
			if (byte_0 > byte_1)
			{
				byte_1 = byte_0;
			}
			return byte_1;
		}
	}
}
